package net.gldwork.facadegen.util;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import net.gldwork.facadegen.annotations.ExcludeInFacade;
import net.gldwork.facadegen.annotations.Facade;
import net.gldwork.srcpdr.Field;
import net.gldwork.srcpdr.Method;
import net.gldwork.srcpdr.SourceProducer;

import org.apache.commons.lang3.StringUtils;

import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.apt.Filer;
import com.sun.mirror.apt.Messager;
import com.sun.mirror.declaration.ClassDeclaration;
import com.sun.mirror.declaration.MethodDeclaration;
import com.sun.mirror.declaration.Modifier;
import com.sun.mirror.declaration.ParameterDeclaration;
import com.sun.mirror.declaration.TypeDeclaration;
import com.sun.mirror.type.ClassType;
import com.sun.mirror.type.ReferenceType;
import com.sun.mirror.type.TypeMirror;

public class FacadeGenUtil {
	private static List<String> steps = new LinkedList<String>();

	public static void printStep(String step, AnnotationProcessorEnvironment env) {

		try {
			steps.add(step);
			Filer filer = env.getFiler();
			PrintWriter pw = filer.createSourceFile("ssd.Steps");
			pw.print(StringUtils.join(steps, "\n"));
			pw.close();
		} catch (IOException e) {

			e.printStackTrace();
		}
	}

	public static void messageStackTrace(Messager messager, Exception e) {
		StringWriter sw = new StringWriter();
		PrintWriter pw = new PrintWriter(sw);
		e.printStackTrace(pw);
		messager.printError(sw.toString());
	}

	public static String[] splitQName(String qName) {
		int lastDot = qName.lastIndexOf('.');
		String pkg = qName.substring(0, lastDot);
		String className = qName.substring(lastDot + 1);
		return new String[] { pkg, className };
	}

	public static String firstLower(String o) {
		char x = (char) (o.charAt(0) + 32);
		return x + o.substring(1);
	}

	public static Facade findFacade(TypeDeclaration typeD) {
		if (typeD == null)
			return null;

		Facade facade = typeD.getAnnotation(Facade.class);
		if (facade != null)
			return facade;

		if (typeD instanceof ClassDeclaration) {
			ClassDeclaration classD = (ClassDeclaration) typeD;
			ClassType superC = classD.getSuperclass();
			if (superC != null) {
				facade = superC.getDeclaration().getAnnotation(Facade.class);
				if (facade != null)
					return facade;
			}

		}
		return null;

	}

	public static void handleTypeD(TypeDeclaration typeD, SourceProducer sp) {

		String oneTypeRef = FacadeGenUtil.firstLower(typeD.getSimpleName());
		if (sp.containsImport(typeD.getQualifiedName())) {
			sp.removeFieldByName(oneTypeRef);
			sp.removeMethodByPrefix(typeD.getSimpleName());
		}

		sp.addImport(typeD.getQualifiedName());

		/*
		 * 生成Method调用
		 */

		Collection<? extends MethodDeclaration> methods = typeD.getMethods();
		boolean methodAdded = false;
		for (MethodDeclaration md : methods) {

			if (md.getAnnotation(ExcludeInFacade.class) != null)
				continue;
			Collection<Modifier> mirs = md.getModifiers();
			if (mirs.contains(Modifier.PUBLIC)
					&& !mirs.contains(Modifier.STATIC)) {
				/*
				 * 非静态public的方法会添加进来
				 */
				TypeMirror rvType = md.getReturnType();
				Method m = new Method(typeD.getSimpleName() + "_"
						+ md.getSimpleName(), rvType.toString(),
						SourceProducer.PUBLIC);

				/*
				 * 参数
				 */

				Collection<ParameterDeclaration> params = md.getParameters();

				List<String> ps = new LinkedList<String>();
				for (ParameterDeclaration param : params) {

					m.addParam(param.getType().toString(),
							param.getSimpleName());
					ps.add(param.getSimpleName());

				}

				Collection<ReferenceType> throwTypes = md.getThrownTypes();
				for (ReferenceType throwType : throwTypes) {
					m.addThrow(throwType.toString());
				}
				StringBuffer body = new StringBuffer();

				if (!"void".equals(rvType.toString())) {
					body.append("return ");
				}
				body.append(oneTypeRef).append(".").append(md.getSimpleName())
						.append("(").append(StringUtils.join(ps, ","))
						.append(");");
				m.addBodyLine(body.toString());
				sp.addMethod(m);
				methodAdded = true;
			}

		}
		if (methodAdded) {
			/*
			 * 添加field
			 */

			Field field = new Field(SourceProducer.PRIVATE,
					typeD.getSimpleName(), oneTypeRef);
			field.addAnnotation("@EJB");
			sp.addField(field);
		}
	}
}
